# -*- coding: utf-8 -*-
from django.forms.models import modelformset_factory
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, Http404
from django.db import models, transaction
from django.db.models import Q
from django.forms import forms
from django.shortcuts import get_object_or_404
from skoolmaster.general.common import render_template, student_required, lecturer_or_administrator_required, lecturer_required, show_model_results_view, check_lecturer_subjects, administrator_required,\
  InsufficientAuthorisation, student_or_administrator_required
from skoolmaster.exams.forms import ExamApplicationForm, SelectExamForm, EditExamForm, InsertExamForm, ExamApplicationFormAdmin, ExamManualApplicationForm
from skoolmaster.exams.models import ExamApplication, Exam, TooManyApplications, TooManyApplicationsThisYear, AlreadyApplied, RecentApplicationTooClose, ApplicationDateHasExpired,\
  MustPay
from skoolmaster.syllabus.models import UniversityProgram, SchoolYear, Subject
from skoolmaster.general.models import Enrollment, UserProfile,\
  EnrollmentSubject
from skoolmaster.general.search import subject_search_logic, exam_search_logic, exam_student_search_logic, exam_app_search_logic, student_search_logic, subject_grades_search_logic
from django.contrib.auth.models import User
import datetime
import time
from collections import defaultdict
from operator import itemgetter, attrgetter

@login_required
@lecturer_or_administrator_required
def exam_results(request, subject = None, exam = None, type = None):
  """
  Display exam results.
  """
  if subject:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, subject)
    
    try:
      exams = Exam.objects.filter(subject = subject, locked = False)
      if not request.user.get_profile().is_administrator():
        exams = exams.filter(examiners = request.user)
    except:
      raise Http404
    return show_model_results_view(request, 'exams/results/dates.html',
      queryset = exams,
      fields = [
        { 'desc' : u"Datum", 'name' : "date" },
        { 'desc' : u"Izpraševalec", 'name' : "name" }        
      ],
      show_search = True,
      search_logic = exam_search_logic,
      vars = {'subject' : get_object_or_404(Subject, pk = subject), 'type' : type}
    )
  elif exam:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, get_object_or_404(Exam, pk = exam).subject)
    
    try:
      exam_apps = ExamApplication.objects.filter(exam = exam)
    except:
      raise Http404
    
    if type == 'points':
      return show_model_results_view(request, 'exams/results/points.html',
        queryset = exam_apps,
        fields = [
          { 'desc' : u"Vpisna številka", 'name' : "student__userprofile__student_number" },
          { 'desc' : u"Priimek in ime", 'name' : "student__userprofile__surname" },
          { 'desc' : u"Točke (od %d)" % Exam.objects.get(pk = exam).max_points, 'name' : "points" }
        ],
        show_search = True,
        search_logic = exam_student_search_logic,
        vars = {'exam' : Exam.objects.get(pk = exam), 'type' : type},
        default_sort_field = 'student__userprofile__surname'
      )
    # Grades
    elif type == 'grades':
      exam = Exam.objects.get(pk = exam)
      fields = [
        { 'desc' : u"Vpisna številka", 'name' : "student__userprofile__student_number" },
        { 'desc' : u"Priimek in ime", 'name' : "student__userprofile__surname" }
      ]
      if exam.subject.grade_mode in [1, 3]:
        fields.append({ 'desc' : u"Ocena izpita", 'name' : "grade_exam" })
      if exam.subject.grade_mode in [1, 2]: 
        fields.append({ 'desc' : u"Ocena vaj", 'name' : "grade_tut" })
        
      return show_model_results_view(request, 'exams/results/grades.html',
        queryset = exam_apps,
        fields = fields,
        show_search = True,
        search_logic = exam_student_search_logic,
        vars = {'exam' : exam, 'type' : type },
        default_sort_field = 'student__userprofile__surname'
      )
    # Applications
    else:
      return show_model_results_view(request, 'exams/results/applications.html',
        queryset = exam_apps,
        fields = [
          { 'desc' : u"Vpisna številka", 'name' : "student__userprofile__student_number" },
          { 'desc' : u"Priimek", 'name' : "student__userprofile__surname" },
          { 'desc' : u"Ime", 'name' : "student__userprofile__name" },
          { 'desc' : u"Leto", 'name' : "" },
          { 'desc' : u"Polaganja", 'name' : "" },
          { 'desc' : u"Odštevanja", 'name' : "" }
        ],
        show_search = True,
        search_logic = exam_student_search_logic,
        vars = {'exam' : Exam.objects.get(pk = exam), 'type' : type },
        default_sort_field = 'student__userprofile__surname'
      )
  
  # No parameter
  subject_queryset = Subject.objects.all()
  if not request.user.get_profile().is_administrator():
    subject_queryset = Subject.objects.filter(lecturers = request.user)
  
  return show_model_results_view(request, 'exams/results/subjects.html',
    queryset = subject_queryset,
    fields = [
      { 'desc' : u"Predmet", 'name' : "name" },
      { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
    ],
    show_search = True,
    search_logic = subject_search_logic,
    vars = {'type' : type }
  )

@login_required
@lecturer_or_administrator_required
def exam_manual_application(request, student = None, subject = None):
  """
  Manual exam application entry.
  """
  if student:
    # Fetch student
    student = get_object_or_404(UserProfile, pk = student)
    
    if subject:
      # Fetch subject
      subject = get_object_or_404(Subject, pk = subject)
      
      # Ensure proper lecturer access
      check_lecturer_subjects(request, subject)
      
      # Show the actual form for manual exam application entry
      if request.method == 'POST':
        form = ExamManualApplicationForm(student, subject, request.POST)
        if form.is_valid():
          form.save()
          
          # Notify user and redirect
          request.user.message_set.create(message = u"0|Uspešno ste dodali oceno študenta preko prijavnice!")
          return HttpResponseRedirect(reverse('exam_manual'))
      else:
        form = ExamManualApplicationForm(student, subject)
      
      return render_template(request, 'exams/manual/application.html', {
        'form' : form,
        'student' : student,
        'subject' : subject
      })
    
    # Get a list of subjects that the user has already passed (for later exclusion)
    passed_subjects = student.subjects.filter(
      exams__examapplication__passed = True,
      exams__examapplication__student = student
    ).distinct()
    
    # Show a list for subject selection
    subject_queryset = student.subjects.exclude(pk__in = passed_subjects)
    if not request.user.get_profile().is_administrator():
      subject_queryset = subject_queryset.filter(lecturers = request.user)
    
    return show_model_results_view(request, 'exams/manual/subjects.html',
      queryset = subject_queryset,
      fields = [
        { 'desc' : u"Predmet", 'name' : "name" },
        { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
      ],
      show_search = True,
      search_logic = subject_search_logic,
      vars = {
        'student' : student
      }
    )
  
  # Show a list for student selection
  return show_model_results_view(request, 'exams/manual/students.html',
    queryset = UserProfile.objects.exclude(student_number = None),
    fields = [
      { 'desc' : u"Vpisna št.", 'name' : "student_number" },
      { 'desc' : u"Priimek", 'name' : "surname" },
      { 'desc' : u"Ime", 'name' : "name" }
    ],
    show_search = True,
    search_logic = student_search_logic,
    default_sort_field = 'surname'
  )

@login_required
@lecturer_or_administrator_required
def exam_insert_grades(request, subject = None, exam = None):
  """
  Insert exam results.
  """
  
  
  if request.method == 'POST':
      try:
          exam_apps = ExamApplication.objects.filter(exam = exam)
      except:
          raise Http404
      exam_obj = Exam.objects.get(pk=exam)
      errors=[]    
      for exam_app in exam_apps:
          student=UserProfile.objects.get(user=exam_app.student)
          stud_num = student.student_number
          error = 0
          if request.POST.has_key(stud_num) and stud_num!=None:
              new_points=request.POST.get(stud_num)
              if new_points!="":
                  print new_points      
                  if new_points != "VP": 
                      try:
                          points = int(new_points)
                      except:
                          errors.append(stud_num)
                          error=1
                      if error!=1:    
                          if points < 0 or points > exam_obj.max_points:
                              errors.append(stud_num)
                              error=1
                  if error != 1:
                      exam_obj.has_grades=True
                      exam_obj.save()
                      if new_points == "VP":
                          exam_app.points = -1
                          exam_app.save()
                          exam_app.delete()
                      else:        
                          exam_app.points = points
                          exam_app.save()
                          
      if errors:
          
          request.user.message_set.create(message = u"2|Nepravilen vnos točk. Vnesite število točk med 0 in %s!"% (exam_obj.max_points))
          return render_template(request, 'exams/grades/insert_grades.html', 
                            {"exam":Exam.objects.get(pk = exam),"exam_apps":exam_apps,"errors":errors})
                        
                                 
      request.user.message_set.create(message = u"0|Uspešno ste vnesli rezultate za izpitni rok: %s !" % (Exam.objects.get(pk=exam)))
      
      return HttpResponseRedirect(reverse('exam_grades_insert',args=[exam]))
  
  
  if subject:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, subject)
    
    try:
      exams = Exam.objects.filter(subject = subject, date__lt = datetime.datetime.now())
      if not request.user.get_profile().is_administrator():
        exams = exams.filter(examiners = request.user)
    except:
      raise Http404
    return show_model_results_view(request, 'exams/grades/dates.html',
      queryset = exams,
      fields = [

        { 'desc' : u"Datum", 'name' : "date" },
        { 'desc' : u"Izpraševalec/ci", 'name' : "name" }

      ],
      show_search = True,
      search_logic = exam_search_logic,
      vars = {'subject' : Subject.objects.get(pk = subject)}
    )
    
  elif exam:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, get_object_or_404(Exam, pk = exam).subject)
    
    try:
      exam_apps = ExamApplication.objects.filter(exam = exam)
    except:
      raise Http404
    errors=[]
    return render_template(request, 'exams/grades/insert_grades.html', {"exam":Exam.objects.get(pk = exam),"exam_apps":exam_apps,"errors":errors}) 
 
  # No parameter
  subject_queryset = Subject.objects.all()
  if not request.user.get_profile().is_administrator():
    subject_queryset = Subject.objects.filter(lecturers = request.user)
  
  return show_model_results_view(request, 'exams/grades/subjects.html',
    queryset = subject_queryset,
    fields = [
      { 'desc' : u"Predmet", 'name' : "name" },
      { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
    ],
    show_search = True,
    search_logic = subject_search_logic
  )
  
  
@login_required
@lecturer_or_administrator_required  
def exam_insert_finalgrades(request, subject = None, exam = None):
  """
  Insert exam final grades.
  """
  if request.method == 'POST':
      try:
          exam_apps = ExamApplication.objects.filter(exam = exam).order_by("student__userprofile__surname")
      except:
          raise Http404
      exam_obj = Exam.objects.get(pk=exam)
      errors=[]
      invalid_entry = False
      invalid_grade_mode = False
      
      for exam_app in exam_apps:
          has_grade_exam = False
          has_grade_tut = False
          exam_grade = 0
          tut_grade = 0
          student=UserProfile.objects.get(user=exam_app.student)
          stud_num = student.student_number
          tut_stud_num="tut"+stud_num
          error=0
          if request.POST.has_key(tut_stud_num) and stud_num!=None:
              new_tut_grade=request.POST.get(tut_stud_num)
              if new_tut_grade!="":
                  try:
                      tut_grade = int(new_tut_grade)
                  except:
                      errors.append(stud_num)
                      error=1
                      invalid_entry = True
                  if error!=1:    
                      if tut_grade < 5 or tut_grade > 10:
                          errors.append(stud_num)
                          error=1
                          invalid_entry = True
                  if error != 1:
                      has_grade_tut = True
                      exam_app.grade_tut = tut_grade
          
          if error == 0:
              exam_stud_num="exam"+stud_num
              if request.POST.has_key(exam_stud_num) and stud_num!=None:
                  new_exam_grade=request.POST.get(exam_stud_num)
                  if new_exam_grade!="":
                      print new_exam_grade      
                      try:
                          exam_grade = int(new_exam_grade)
                      except:
                          errors.append(stud_num)
                          error=1
                          invalid_entry = True
                      if error!=1:    
                          if exam_grade < 5 or exam_grade > 10:
                              errors.append(stud_num)
                              error=1
                              invalid_entry = True
                      if error != 1:
                          has_grade_exam = True
                          exam_app.grade_exam = exam_grade
          
          if exam_obj.subject.has_grade_tut() and exam_obj.subject.has_grade_exam():
            if has_grade_tut != has_grade_exam:
              invalid_grade_mode = True
              errors.append(stud_num)
              error = 1
            else:
              passed_exam = exam_grade > 5
              passed_tut = tut_grade > 5
                
              if passed_exam != passed_tut:
                invalid_grade_mode = True
                errors.append(stud_num)
                error = 1
          
          if error != 1:
            exam_obj.has_grades=True
            exam_obj.save()
            exam_app.save()
                          
      if errors:
          if invalid_entry:
            request.user.message_set.create(message = u"2|Nepravilen vnos ocene. Vnesite oceno med 5 in 10!")
          if invalid_grade_mode:
            request.user.message_set.create(message = u"2|Obe oceni morata biti ali pozitivni ali negativni!")
          return render_template(request, 'exams/final_grades/insert_finalgrades.html', 
                            {"exam":Exam.objects.get(pk = exam),"exam_apps":exam_apps,"errors":errors})
                                 
      request.user.message_set.create(message = u"0|Uspešno ste vnesli rezultate za izpitni rok: %s !" % (Exam.objects.get(pk=exam)))
      return HttpResponseRedirect(reverse('exam_finalgrades_insert',args=[exam]))
  
  
  if subject:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, subject)
    
    try:
      exams = Exam.objects.filter(subject = subject, date__lt = datetime.datetime.now())
      if not request.user.get_profile().is_administrator():
        exams = exams.filter(examiners = request.user)
    except:
      raise Http404
    return show_model_results_view(request, 'exams/final_grades/dates.html',
      queryset = exams,
      fields = [

        { 'desc' : u"Datum", 'name' : "date" },
        { 'desc' : u"Izpraševalec/ci", 'name' : "name" }

      ],
      show_search = True,
      search_logic = exam_search_logic,
      vars = {'subject' : Subject.objects.get(pk = subject)}
    )
    
  elif exam:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, get_object_or_404(Exam, pk = exam).subject)
    
    try:
      exam_apps = ExamApplication.objects.filter(exam = exam).order_by("student__userprofile__surname")
    except:
      raise Http404
    errors=[]
    return render_template(request, 'exams/final_grades/insert_finalgrades.html', {"exam":Exam.objects.get(pk = exam),"exam_apps":exam_apps,"errors":errors}) 
 
  # No parameter
  subject_queryset = Subject.objects.all()
  if not request.user.get_profile().is_administrator():
    subject_queryset = Subject.objects.filter(lecturers = request.user)
  
  return show_model_results_view(request, 'exams/final_grades/subjects.html',
    queryset = subject_queryset,
    fields = [
      { 'desc' : u"Predmet", 'name' : "name" },
      { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
    ],
    show_search = True,
    search_logic = subject_search_logic
  )
  



@login_required
@student_or_administrator_required
def exam_list(request, printout = 'all', student_number = None):  
  """
  Display exam list.
  """    
  
  def average_grades(modules, exam_apps):
    sum_exams = sum_tuts = num_exams = num_tuts = ects = 0
    for _, subjects in modules.items():
      for subject in subjects:     
        for exam_app in exam_apps[subject]:
        
          if exam_app.grade_exam and exam_app.grade_exam > 5:
            sum_exams = sum_exams + exam_app.grade_exam
            num_exams = num_exams + 1
            ects = ects + exam_app.exam.subject.ects
            if exam_app.grade_tut:
              sum_tuts = sum_tuts + exam_app.grade_tut
              num_tuts = num_tuts + 1
            
    return ((float(sum_exams) / num_exams) if num_exams > 0 else 0, 
            (float(sum_tuts) / num_tuts) if num_tuts > 0 else 0,
            ects)
  
  
  if student_number == None:
    if not request.user.get_profile().is_administrator():    
      raise InsufficientAuthorisation
    
    return show_model_results_view(request, 'exams/list/students.html',
      queryset = UserProfile.objects.exclude(student_number = None),
      fields = [
        { 'desc' : u"Vpisna št.", 'name' : "student_number" },
        { 'desc' : u"Priimek", 'name' : "surname" },
        { 'desc' : u"Ime", 'name' : "name" },     
      ],
      vars = {},
      show_search = True,
      search_logic = student_search_logic,
      default_sort_field = 'surname'
    )
  else:
    if not request.user.get_profile().is_administrator() and student_number != request.user.get_profile().student_number:    
      raise InsufficientAuthorisation
    
    student = get_object_or_404(User, userprofile__student_number = student_number)    
  
  if printout != None and printout not in ['all','last']:
    raise Http404

  enrollment_list = Enrollment.objects.select_related().filter(student__user = student).order_by('university_program','-school_year','study_year')
  
  enrs = []  
  exam_apps = {}

  for enr in enrollment_list:      
    subject_list = EnrollmentSubject.objects.filter(enrollment = enr).order_by('-module')
    
    enr.ects_total = 0
    modules = defaultdict(list)
    for enr_subject in subject_list:
      modules[enr_subject.module].append(enr_subject.subject)
              
      enr.ects_total = enr.ects_total + enr_subject.subject.ects
      exam_apps[enr_subject.subject] = []
      for exam_app in ExamApplication.objects.filter(
          Q(grade_exam__isnull = False) | Q(grade_tut__isnull = False),          
          student = student, 
          exam__subject = enr_subject.subject,           
          ):
        exam_apps[enr_subject.subject].append(exam_app)
         
    enr.avg_exam, enr.avg_tut, enr.ects_achieved = average_grades(modules, exam_apps)    
    enr.avg_all = (enr.avg_exam + enr.avg_tut) / 2.0
         
    enrs.append((enr,dict(modules)))
                  
  return render_template(request, 'exams/list/exam_list_%s.html' % printout, {
    'enrollment_list' : enrs, 'exams':exam_apps, 'student' : student,
    'print_view'       : request.GET.get('print', ''), })




@login_required
@lecturer_or_administrator_required
def exam_delete(request, subject=None, exam=None):
  """
  Delete an exam.
  """
     
  if request.method == 'POST':
      exam = get_object_or_404(Exam, pk = exam)
      if 'Accept' in request.POST:
          exam.delete() 
          request.user.message_set.create(message = u"0|Izbrisali ste izpitni rok za predmet %s, dne %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year))
          return HttpResponseRedirect(reverse('exam_delete',args=[subject]))
      
      if 'Cancel' in request.POST:
          request.user.message_set.create(message = u"2|Preklicali ste dejanje!")
          return HttpResponseRedirect(reverse('exam_delete',args=[subject]))
  
  
  if exam:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, get_object_or_404(Exam, pk = exam).subject)
    exam = get_object_or_404(Exam, pk = exam)
    
    try:
      exam_apps = ExamApplication.objects.filter(exam = exam).order_by("student__userprofile__surname")
    except:
      raise Http404
    prijave = False
    if exam_apps.count() != 0:
        prijave = True
        
    if prijave:
        try:
            exams = Exam.objects.filter(subject = subject, has_grades = False)
            if not request.user.get_profile().is_administrator():
                exams = exams.filter(examiners = request.user)
        except:
            raise Http404
        
        request.user.message_set.create(message = u"1|Za ta rok obstajajo prijave!")
        return show_model_results_view(request, 'exams/delete/exam_dates.html',
                                       queryset = exams,
                                       fields = [

                                       { 'desc' : u"Datum", 'name' : "date" },
                                       { 'desc' : u"Izpraševalec/ci", 'name' : "name" },
                                       { 'desc' : u"Brisanje", 'name' : "" }

                                       ],
                                       show_search = True,
                                       search_logic = exam_search_logic,
                                       vars = {'subject' : Subject.objects.get(pk = subject),
                                               'warning' : 1}
                                       )
    else:
        exam.delete() 
        request.user.message_set.create(message = u"0|Izbrisali ste izpitni rok za predmet %s, dne %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year))
        return HttpResponseRedirect(reverse('exam_delete',args=[subject]))
        


  elif subject:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, subject)
    
    try:
      exams = Exam.objects.filter(subject = subject, has_grades = False)
      if not request.user.get_profile().is_administrator():
        exams = exams.filter(examiners = request.user)
    except:
      raise Http404
    return show_model_results_view(request, 'exams/delete/exam_dates.html',
      queryset = exams,
      fields = [

        { 'desc' : u"Datum", 'name' : "date" },
        { 'desc' : u"Izpraševalec/ci", 'name' : "name" },
        { 'desc' : u"Brisanje", 'name' : "" }

      ],
      show_search = True,
      search_logic = exam_search_logic,
      vars = {'subject' : Subject.objects.get(pk = subject)}
    )
    
  
 
  

  # No parameter
  subject_queryset = Subject.objects.all()
  if not request.user.get_profile().is_administrator():
    subject_queryset = Subject.objects.filter(current_lecturers = request.user)
  
  return show_model_results_view(request, 'exams/delete/exam_subj_search.html',
    queryset = subject_queryset,
    fields = [
      { 'desc' : u"Predmet", 'name' : "name" },
      { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
    ],
    show_search = True,
    search_logic = subject_search_logic
  )


@login_required
@lecturer_or_administrator_required
def exam_edit(request, subject=None, exam = None):
  """
  Edit an exam.
  """
  if request.method == 'POST':

    if 'Accept' in request.POST:
        
        form = EditExamForm(subject, exam)
        selected_subject = Subject.objects.get(pk=subject)
        exams = Exam.objects.filter(subject = Subject.objects.get(id=subject),school_year=SchoolYear.objects.latest())
        return render_template(request, 'exams/edit/exam_insert.html', {'form' : form, "subject":selected_subject,"exams":exams})
       
    if 'Cancel' in request.POST:
        request.user.message_set.create(message = u"2|Preklicali ste dejanje!")
        return HttpResponseRedirect(reverse('exam_edit',args=[subject]))
    
    examiners = request.POST.get('examiners')
    place = request.POST.get('place')
    points = request.POST.get('max_points')
    school_year = request.POST.get('school_year')
    date1 = request.POST.get('datetime_exam_0')
    time1 = request.POST.get('datetime_exam_1')
    
    
    if request.POST.has_key('datetime_exam_0') and request.POST.has_key('datetime_exam_1'):
      error=0    
      # validation for inserted date and time
      if date1=="" or time1=="":
          request.user.message_set.create(message = u"2|Vnesite datum in uro!")
          error=1     
      date = date1 +" "+ time1
      try: 
          time.strptime(date, "%Y-%m-%d %H:%M:%S")
          
          # validation if date matches school_year
          schoolyear_obj = SchoolYear.objects.get(pk=school_year)
          date_obj=datetime.datetime(*(time.strptime(date, "%Y-%m-%d %H:%M:%S")[0:6]))  
          if date_obj.date() < schoolyear_obj.start or date_obj.date() > schoolyear_obj.end:
              request.user.message_set.create(message = u"2|Vnešeni datum je izven izbranega šolskega leta!")
              error=1
              
          # validation for duplicated exams
          exams = Exam.objects.filter(subject = Subject.objects.get(id=subject),school_year=SchoolYear.objects.get(pk=school_year),
                                  date__contains = date1, examiners=examiners).exclude(id=exam)
         
          if exams.count()>0:
              request.user.message_set.create(message = u"2|Izpitni rok za ta predmet na ta dan že obstaja!")
              error=1   
               
      except:
          request.user.message_set.create(message = u"2|Napačno vnešen datum oz. ura, privzeta je obstoječa vrednost!")
          error=1

      # validation for place
      if place=="":
          request.user.message_set.create(message = u"2|Vnesite prostor, privzeta je obstoječa vrednost!")
          error=1  
          
      # validation for points
      if points=="":
          request.user.message_set.create(message = u"2|Vnesite največje možno število točk izpita, privzeta je obstoječa vrednost!")
          error=1  
          
      # validation for examiners
      if examiners==None:
          request.user.message_set.create(message = u"2|Izberite izpraševalce, privzeta je obstoječa vrednost!")
          error=1
          

      # if no validation errors
      if error==0: 
          exam = Exam.objects.get(id=exam)
          exam.school_year = SchoolYear.objects.get(pk=school_year)
          exam.date = date
          exam.location = place
          exam.max_points = points 
          exam.save()
          exam.examiners.clear()
          for examiner_id in request.POST.getlist('examiners'):
              exam.examiners.add(User.objects.get(pk = examiner_id))
              exam.save()          
                    
          subject_name=Subject.objects.get(id=subject).name
          request.user.message_set.create(message = u"0|Uspešno ste spremenili izpitni rok za predmet %s, dne %s.%s.%s ob %s uri!" % (subject_name, 
                                                                                                                             date_obj.day,
                                                                                                                             date_obj.month,
                                                                                                                             date_obj.year, 
                                                                                                                             date_obj.time()))

          return HttpResponseRedirect(reverse('exam_edit',args=[subject]))
       
      else:
         form = EditExamForm(subject, exam)
         selected_subject = Subject.objects.get(pk=subject)
         exams = Exam.objects.filter(subject = Subject.objects.get(id=subject),school_year=SchoolYear.objects.latest())
         return render_template(request, 'exams/edit/exam_insert.html', {'form' : form, "subject":selected_subject,"exams":exams})
          

  if exam:

    try:
      exam_apps = ExamApplication.objects.filter(exam = exam).order_by("student__userprofile__surname")
    except:
      raise Http404
  
    prijave = False
    if exam_apps.count() != 0:
        prijave = True
        
    if prijave:
        request.user.message_set.create(message = u"1|Za ta rok obstajajo prijave!")
        # Ensure proper lecturer access
        check_lecturer_subjects(request, subject)
    
        try:
            exams = Exam.objects.filter(subject = subject, has_grades = False)
            if not request.user.get_profile().is_administrator():
                exams = exams.filter(examiners = request.user)
        except:
            raise Http404
        return show_model_results_view(request, 'exams/edit/exam_dates.html',
                                       queryset = exams,
                                       fields = [

                                       { 'desc' : u"Datum", 'name' : "date" },
                                       { 'desc' : u"Izpraševalec/ci", 'name' : "name" },
                                       { 'desc' : u"Prostor", 'name' : "name" },
                                       { 'desc' : u"Možne točke", 'name' : "name" },
                                       { 'desc' : u"Spremeni", 'name' : "" }

                                       ],
                                       show_search = True,
                                       search_logic = exam_search_logic,
                                       vars = {'subject' : Subject.objects.get(pk = subject),
                                               'warning' : 1}
                                       )
    else:
        form = EditExamForm(subject, exam)
        selected_subject = Subject.objects.get(pk=subject)
        exams = Exam.objects.filter(subject = Subject.objects.get(id=subject),school_year=SchoolYear.objects.latest())
        return render_template(request, 'exams/edit/exam_insert.html', {'form' : form, "subject":selected_subject,"exams":exams})           
 
            

  elif subject:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, subject)
    
    try:
      exams = Exam.objects.filter(subject = subject, has_grades = False)
      if not request.user.get_profile().is_administrator():
        exams = exams.filter(examiners = request.user)
    except:
      raise Http404
    return show_model_results_view(request, 'exams/edit/exam_dates.html',
      queryset = exams,
      fields = [

        { 'desc' : u"Datum", 'name' : "date" },
        { 'desc' : u"Izpraševalec/ci", 'name' : "name" },
        { 'desc' : u"Prostor", 'name' : "name" },
        { 'desc' : u"Možne točke", 'name' : "name" },
        { 'desc' : u"Spremeni", 'name' : "" }

      ],
      show_search = True,
      search_logic = exam_search_logic,
      vars = {'subject' : Subject.objects.get(pk = subject)}
    )
  

  # No parameter
  subject_queryset = Subject.objects.all()
  if not request.user.get_profile().is_administrator():
    subject_queryset = Subject.objects.filter(current_lecturers = request.user)
  
  return show_model_results_view(request, 'exams/edit/exam_subj_search.html',
    queryset = subject_queryset,
    fields = [
      { 'desc' : u"Predmet", 'name' : "name" },
      { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
    ],
    show_search = True,
    search_logic = subject_search_logic
  )


@login_required
@lecturer_or_administrator_required
def exam_insert(request, subject=None):
  """
  Insert an exam.
  """
  if request.method == 'POST':
    
    examiners = request.POST.get('examiners')
    place = request.POST.get('place')
    points = request.POST.get('max_points')
    school_year = request.POST.get('school_year')
    date1 = request.POST.get('datetime_exam_0')
    time1 = request.POST.get('datetime_exam_1')
    
    
    if request.POST.has_key('datetime_exam_0') and request.POST.has_key('datetime_exam_1'):
      error=0    
      # validation for inserted date and time
      if date1=="" or time1=="":
          request.user.message_set.create(message = u"2|Vnesite datum in uro!")
          error=1     
      date = date1 +" "+ time1
      try: 
          time.strptime(date, "%Y-%m-%d %H:%M:%S")
          
          # validation if date matches school_year
          schoolyear_obj = SchoolYear.objects.get(pk=school_year)
          date_obj=datetime.datetime(*(time.strptime(date, "%Y-%m-%d %H:%M:%S")[0:6]))  
          if date_obj.date() < schoolyear_obj.start or date_obj.date() > schoolyear_obj.end:
              request.user.message_set.create(message = u"2|Vnešeni datum je izven izbranega šolskega leta!")
              error=1
              
          # validation for duplicated exams
          exams = Exam.objects.filter(subject = Subject.objects.get(id=subject),school_year=SchoolYear.objects.get(pk=school_year),
                                  date__contains = date1, examiners=examiners)
          
          
          if exams.count()>0:
              request.user.message_set.create(message = u"2|Izpitni rok za ta predmet na ta dan že obstaja!")
              error=1   
               
      except:
          request.user.message_set.create(message = u"2|Napačno vnešen datum oz. ura!")
          error=1

      # validation for place
      if place=="":
          request.user.message_set.create(message = u"2|Vnesite prostor!")
          error=1  
          
      # validation for points
      if points=="":
          request.user.message_set.create(message = u"2|Vnesite največje možno število točk izpita!")
          error=1  
          
      # validation for examiners
      if examiners==None:
          request.user.message_set.create(message = u"2|Izberite izpraševalce!")
          error=1
          

      # if no validation errors
      if error==0: 
          exam = Exam(subject = Subject.objects.get(id=subject), school_year = SchoolYear.objects.get(pk=school_year),
               date = date, location = place, max_points = points)
          exam.save() 
          exam.examiners.clear()
          for examiner_id in request.POST.getlist('examiners'):
              exam.examiners.add(User.objects.get(pk = examiner_id))
              exam.save()          
                    
          subject_name=Subject.objects.get(id=subject).name
          request.user.message_set.create(message = u"0|Vnesli ste izpitni rok za predmet %s, dne %s.%s.%s ob %s uri!" % (subject_name, 
                                                                                                                             date_obj.day,
                                                                                                                             date_obj.month,
                                                                                                                             date_obj.year, 
                                                                                                                             date_obj.time()))

          return HttpResponseRedirect(reverse('exam_insert_form',args=[subject])) 
  

  if subject:
    # Ensure proper lecturer access
    check_lecturer_subjects(request, subject)
    subject_id=subject
    if request.method == 'POST' :
        form = InsertExamForm(subject_id, request.POST)
    else:
        form = InsertExamForm(subject_id)
    selected_subject = Subject.objects.get(pk=subject)
    exams = Exam.objects.filter(subject = Subject.objects.get(id=subject),school_year=SchoolYear.objects.latest())
    return render_template(request, 'exams/insert/exam_insert.html', {'form' : form, "subject":selected_subject,"exams":exams})
  

  # No parameter
  subject_queryset = Subject.objects.all()
  if not request.user.get_profile().is_administrator():
    subject_queryset = Subject.objects.filter(current_lecturers = request.user)
  
  return show_model_results_view(request, 'exams/insert/exam_subj_search.html',
    queryset = subject_queryset,
    fields = [
      { 'desc' : u"Predmet", 'name' : "name" },
      { 'desc' : u"Predavatelji", 'name' : "current_lecturers" }
    ],
    show_search = True,
    search_logic = subject_search_logic
  )

@login_required
@student_required
def exam_signout(request, exam):
  """
  A view for handling exam signout.
  """
  exam = get_object_or_404(Exam, pk = exam)
  
  # Sign out not accepted, as it is too late
  if not exam.can_signout():
    request.user.message_set.create(
      message = u"2|Odjava iz izpita %s, dne %s.%s.%s ni več mogoča!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year)
    )
  else:
    request.user.exam_applications.filter(exam = exam).delete()    
    request.user.message_set.create(
      message = u"0|Uspešno ste se odjavili iz izpita %s, dne %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year)
    )
  
  return HttpResponseRedirect(reverse('exam_signup'))

@login_required
@student_required
def exam_signup(request):
  """
  Display a form for signing up to the exam. 
  """
  if request.method == 'POST' and request.POST.get('exam'):
    form = ExamApplicationForm(request.user, request.POST)
    
    if form.is_valid():      
      exam = form.cleaned_data.get('exam')
      
      if form.cleaned_data.get('must_pay', False):
        request.user.message_set.create(message = u"1|Ta izpit morate plačati! Položnico boste dobili na vaš domači naslov.")          
              
      # Save exam application
      form.save()
      
      # Notify the user and redirect      
      request.user.message_set.create(
        message = u"0|Uspešno ste se prijavili na izpit %s, ki bo %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year)
      )
      return HttpResponseRedirect(reverse('exam_signup'))
  else:
    form = ExamApplicationForm(request.user)    
  
  return show_model_results_view(request, 'exams/signup/exam_signup.html',
    # We show those exam applications for current users that have no grade filled in yet
    queryset = ExamApplication.objects.filter(
      student = request.user, 
      grade_exam__isnull = True,
      grade_tut__isnull = True
    ).order_by('exam__date'),
    fields = [
      { 'desc' : u"Predmet", 'name' : "exam__subject__name" },
      { 'desc' : u"Datum izpita", 'name' : "exam__date" },
      { 'desc' : u"Izpraševalec/ci", 'name' : "exam__examiners" },
      { 'desc' : u"Prostor", 'name' : "exam__location" },
      { 'desc' : u"Odjava", 'name' : "" }
    ],
    show_search = True,
    search_logic = exam_app_search_logic,
    vars = {
      'form': form,
      'exam_queryset' : form.fields['exam'].queryset
    }
  )
  
@login_required
@administrator_required
def exam_signout_admin(request, exam, student):
  """
  A view for handling exam signout by an administrator.
  """
  try:
    selected_stud = UserProfile.objects.get(student_number = student)
  except UserProfile.DoesNotExist:
    selected_stud = None
  
  exam = get_object_or_404(Exam, pk = exam)
  if request.method == 'POST':
    # admin confirmation check:
    if 'Accept' in request.POST:      
      ExamApplication.objects.filter(exam = exam, student=selected_stud).delete()
      request.user.message_set.create(
        message = u"0|Uspešna odjava od izpita %s, dne %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year)
      )        
    
    if 'Cancel' in request.POST:
      request.user.message_set.create(message = u"2|Preklicali ste dejanje!")
  else:
    if not exam.can_signout():
      request.user.message_set.create(message = u"1|Rok za odjavo je potekel!") 
      warning = 2
      form = ExamApplicationFormAdmin(stud_num = student, exam = exam) 
      
      return show_model_results_view(request, 'exams/signup/exam_signup_admin.html', 
        queryset = ExamApplication.objects.filter(
          student = selected_stud,
          grade_exam__isnull = True,
          grade_tut__isnull = True
        ).order_by('exam__date'),
        fields = [
          { 'desc' : u"Predmet", 'name' : "exam__subject__name" },
          { 'desc' : u"Datum izpita", 'name' : "exam__date" },
          { 'desc' : u"Izpraševalec/ci", 'name' : "exam__examiners" },
          { 'desc' : u"Prostor", 'name' : "exam__location" },
          { 'desc' : u"Odjava", 'name' : "" }
        ],
        show_search = True,
        search_logic = exam_app_search_logic,
        vars = {
          'form' : form,
          'exam_queryset' : form.fields['exam'].queryset,
          'student' : selected_stud,
          'warning' : warning
        }
      )
    else:
      ExamApplication.objects.filter(exam = exam, student = selected_stud).delete()    
      request.user.message_set.create(
        message = u"0|Uspešna odjava iz izpita %s, dne %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year)
      )
  
  return HttpResponseRedirect(reverse('exam_signup_admin',args=[student]))

@login_required
@administrator_required
def exam_signup_admin(request, student=None):
  """
  Display a form for signing up to the exam performed by the admin.
  """
  selected_stud=exam=None
  error=warning=warnings=0
  
  if student==None:
    student_number=None
  else:
    student_number=student
    try:
      selected_stud = UserProfile.objects.get(student_number=student_number)
    except:
      request.user.message_set.create(message = u"2|Študenta s tako vpisno številko ni v bazi!")
      error=1
      student_number=None

  if request.method == 'POST':
    error=0
    warning=0
    warnings=0
    student_number = request.POST.get('stud_num')    
    try:
      selected_stud = UserProfile.objects.get(student_number=student_number)
    except:
      request.user.message_set.create(message = u"2|Študenta s tako vpisno številko ni v bazi!")
      error=1
      student_number=None
    
    # if exam chosen:     
    if request.POST.has_key('exam') and request.POST['exam'] != '':
      exam = Exam.objects.get(pk = request.POST.get('exam'))
      
      # Handle warnings
      checks = exam.check_can_take(selected_stud.user, multiple_exceptions = True)
      if 'Accept' in request.POST:
        warning = 0
        print "accepted"
      else:
        for error in checks:
          warning = 1
          
          if error == TooManyApplications:
            request.user.message_set.create(message = u"1|Število polaganj tega izpita je preseženo!")
          elif error == TooManyApplicationsThisYear:
            request.user.message_set.create(message = u"1|Število polaganj tega izpita za to leto je preseženo!")
          elif error == AlreadyApplied:
            request.user.message_set.create(message = u"1|Na izpit za ta predmet je študent že prijavljen!") 
          elif error == RecentApplicationTooClose:
            request.user.message_set.create(message = u"1|Prijava na izpit prej kot 14 dni od zadnjega polaganja!")
          elif error == ApplicationDateHasExpired:
            request.user.message_set.create(message = u"1|Rok prijave je potekel!")
          elif error == MustPay:
            request.user.message_set.create(message = u"1|Študent mora plačati izpit!")
      
      # admin confirmation check
      if 'Cancel' in request.POST:
        request.user.message_set.create(message = u"2|Preklicali ste dejanje!")
        return HttpResponseRedirect(reverse('exam_signup_admin',args = [student_number]))
                                                      
      if warning==0 and error==0:
        # Save exam application
        app = ExamApplication(exam = exam, student = selected_stud.user)
        app.must_pay = (MustPay in checks)
        app.save()
        
        # Display message and redirect
        request.user.message_set.create(
          message = u"0|Študenta ste uspešno prijavili na izpit %s, ki bo %s.%s.%s!" % (exam.subject, exam.date.day, exam.date.month, exam.date.year)
        )
        return HttpResponseRedirect(reverse('exam_signup_admin',args=[student_number]))
              
      if warning==1 and error==0:       
        student_number=student_number
        exam=exam
  
  form = ExamApplicationFormAdmin(stud_num = student_number, exam = exam)
  if selected_stud is not None:
    # We show those exam applications for current users that have no grade filled in yet 
    return show_model_results_view(request, 'exams/signup/exam_signup_admin.html',
      queryset = ExamApplication.objects.filter(
        student = selected_stud,
        grade_exam__isnull = True,
        grade_tut__isnull = True
      ).order_by('exam__date'),
      fields = [
        { 'desc' : u"Predmet", 'name' : "exam__subject__name" },
        { 'desc' : u"Datum izpita", 'name' : "exam__date" },
        { 'desc' : u"Izpraševalec/ci", 'name' : "exam__examiners" },
        { 'desc' : u"Prostor", 'name' : "exam__location" },
        { 'desc' : u"Odjava", 'name' : "" }
      ],
      show_search = True,
      search_logic = exam_app_search_logic,
      vars = {
        'form' : form,
        'exam_queryset' : form.fields['exam'].queryset,
        'student' : selected_stud,
        'warning' : warning,
        'signup' : 1
      }
    )
  
  # No parameter
  return show_model_results_view(request, 'exams/signup/students.html',
    queryset = UserProfile.objects.exclude(student_number = None),
    fields = [
      { 'desc' : u"Vpisna št.", 'name' : "student_number" },
      { 'desc' : u"Priimek", 'name' : "surname" },
      { 'desc' : u"Ime", 'name' : "name" },
    ],
    vars = {},
    show_search = True,
    search_logic = student_search_logic,
    default_sort_field = 'surname'
  )


@login_required
@lecturer_required
def student_grades(request, student_id = None, exam_id = None):
  """
  Allows the lecturer to display grades of students for his subjects.
  """
  if student_id:
    student = get_object_or_404(UserProfile, student_number = student_id)
    return show_model_results_view(request, 'exams/lecturer_grades/grades.html',
      queryset = student.user.exam_applications.filter(exam__examiners = request.user),
      fields = [
        { 'desc' : u"Predmet", 'name' : "exam__subject" },
        { 'desc' : u"Ocena izpita", 'name' : "grade_exam" },
        { 'desc' : u"Ocena vaj", 'name' : "grade_tut" },
        { 'desc' : u"Datum", 'name' : "exam__date" },
      ],
      vars = {'student' : student },
      show_search = True,
      search_logic = subject_grades_search_logic,
      default_sort_field = 'exam__subject'
    )
  # No parameter
  return show_model_results_view(request, 'exams/lecturer_grades/students.html',
    queryset = UserProfile.objects.exclude(student_number = None),
    fields = [
      { 'desc' : u"Vpisna št.", 'name' : "student_number" },
      { 'desc' : u"Priimek", 'name' : "surname" },
      { 'desc' : u"Ime", 'name' : "name" },
    ],
    vars = {},
    show_search = True,
    search_logic = student_search_logic,
    default_sort_field = 'surname'
  )
